#include "wrap.h"
#include <sys/epoll.h>
// 这是一个使用epoll函数来实现阻塞监听的服务器端

#define MAXSIZE 4096
#define SERV_PORT 8888

void epoll_server(){
	// 声明变量
	int listen_fd, clit_fd, i, num,n,j, sockfd;
	struct sockaddr_in serv_addr, clit_addr;
	char buf[MAXSIZE], str[INET_ADDRSTRLEN];	// buf用来存放客户端的通信数据，str用来存放客户端的地址
	socklen_t clit_len;
	ssize_t nready, efd, res;

	// 声明epoll函数所需要的变量, temp_ep为epoll_ctl的参数，epol[]为epoll_wait的参数
	struct epoll_event temp_ep, epol[FOPEN_MAX];

	// 1. 创建socket套接字
	listen_fd = Socket(AF_INET,SOCK_STREAM, 0);

	// 2. 使用bind函数进行绑定
	bzero(&serv_addr, sizeof(serv_addr));
	serv_addr.sin_family = AF_INET;
	serv_addr.sin_port = htons(SERV_PORT);
	serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);

	Bind(listen_fd, (struct sockaddr*)&serv_addr, sizeof(serv_addr));

	// 3. 使用listen函数创建监听上限
	Listen(listen_fd, 1024);

	// 4. 创建epoll模型，efd指向红黑树节点
	efd = epoll_create(FOPEN_MAX);
	if(efd == -1){
		sys_err("epoll_create errer");
	}

	// 指定
	// listen_fd的监听时间为“读”
	temp_ep.events = EPOLLIN;
	temp_ep.data.fd = listen_fd;

	// 将listen_fd及对应结构体设置到树上，efd可找到该树
	res = epoll_ctl(efd, EPOLL_CTL_ADD, listen_fd, &temp_ep);

	if(res == -1){
		sys_err("epoll_ctl error");
	}

	while(1){
		// epoll为server阻塞监听事件，epol为struct epoll_event数组，OPEN_MAX为数组容量，-1表示永久阻塞
		nready = epoll_wait(efd, epol, FOPEN_MAX, -1);
		if(nready == -1){
			sys_err("epoll_wait error");
		}

		for(i = 0; i<nready; ++i){
			// 如果不是“读”事件，则继续循环
			if(!epol[i].events & EPOLLIN){
				continue;
			}
			
			if(epol[i].data.fd == listen_fd){
				clit_len = sizeof(clit_addr);
				clit_fd = Accept(listen_fd, (struct sockaddr*)&clit_addr, &clit_len);

				printf("received from %s at PORT %d\n", inet_ntop(AF_INET, &clit_addr.sin_addr.s_addr, str, sizeof(str)),
								ntohs(clit_addr.sin_port));
				printf("clit_fd %d --- client %d\n", clit_fd, ++num);

				// 将客户端加入到监听节点中
				temp_ep.events = EPOLLIN;
				temp_ep.data.fd = clit_fd;
				res = epoll_ctl(efd, EPOLL_CTL_ADD, clit_fd, &temp_ep);

				if(res == -1){
					sys_err("epoll_ctl error");
				}
			}else{
				sockfd = epol[i].data.fd;
				n = Read(sockfd, buf, MAXSIZE);
				
				// 如果n=0，则关闭客户端与服务器的链接
				if(n == 0){
					res = epoll_ctl(efd, EPOLL_CTL_DEL, sockfd, NULL);	//将该文件描述符从红黑树中删除
					if(res == -1){
						sys_err("epoll_ctl error");
					}
					printf("client[%d] closed connection\n", sockfd);
					close(sockfd);
				}else if(n < 0){
					perror("read n < 0 error:");
					res = epoll_ctl(efd, EPOLL_CTL_DEL, sockfd, NULL);
					close(sockfd);
				}else{
					write(STDOUT_FILENO, buf, n);
					//printf("%s", buf);
					for(j = 0; j<n; ++j){
						buf[j] = toupper(buf[j]);
					}
					Write(sockfd, buf, n);
				}
			}
		}
	}
}		

int main(int argc, char* argv[]){
	epoll_server();
}
